package pool;
import java.util.AbstractCollection;
import java.util.Iterator;
import java.util.LinkedList;

public class GameObjectPool<GameObject> extends AbstractCollection<GameObject> {


    private LinkedList<GameObject> _gameObjects;
    private static int Iterator_Version = 1;

	public GameObjectPool()
	{
		_gameObjects = new LinkedList<GameObject>();
	}
	
	
	@Override
	public Iterator<GameObject> iterator() {
		return (Iterator<GameObject>) new GameObjectPoolIterator();
	}

	@Override
	public int size() {
		return _gameObjects.size();
	}
	
	@Override 
	public boolean add(GameObject element)
	{
		Iterator_Version++;
		_gameObjects.addFirst(element);
		return true;
	}
	
	public void addInactive(GameObject gameObject)
	{
		Iterator_Version++;
		_gameObjects.addLast(gameObject);
	}
	
	public void addActive(GameObject gameObject)
	{
		this.add(gameObject);
	}
	
	public boolean canAddActiveElement()
	{
		return !(((gameObject.GameObject)_gameObjects.peekLast()).isActive());
	}
	
	public GameObject removeInactive() 
	{
		if(!canAddActiveElement()) throw new IllegalStateException("Must check if inactive bullets left!");
		Iterator_Version++;
		return _gameObjects.pollLast();
	}
	
	private class GameObjectPoolIterator implements Iterator<GameObject>
	{
		private int index;
		private boolean justRemoved;
		private int myIteratorVersion;
		
		public GameObjectPoolIterator()
		{
			index = -1;
			justRemoved = false;
			myIteratorVersion = Iterator_Version;
		}
		
		@Override
		public boolean hasNext() 
		{
			checkVersions();
			if(justRemoved)
			{
				return ((gameObject.GameObject) _gameObjects.get(index)).isActive();
			}
			else
			{
				return ( index + 1 < _gameObjects.size() 
						&& ((gameObject.GameObject) _gameObjects.get(index + 1)).isActive()
				   		);
			}
		}

		@Override
		public GameObject next() {
			checkVersions();
			checkCanCallNext();
			incrementVersions();
			GameObject nextElement = getNextElementAndUpdateIndex();
			updateRemoveStatusToReflectNext();
			return nextElement;
		}

		private GameObject getNextElementAndUpdateIndex() {
			if(!justRemoved)
			{
				index++;
			}
			return _gameObjects.get(index);
		}
		
		private void updateRemoveStatusToReflectNext()
		{
			justRemoved = false;
		}

		@Override
		public void remove() {
			checkVersions();
			checkCanCallRemove();
			incrementVersions();
			removeAndDeactivateActiveElement();
			updateRemoveStatusToReflectRemove();
		}
		
		/**
		 * This method takes GameObject elements that are on screen and deactivates them
		 * 
		 * Move the current 'active' element to the end of the container
		 * Remove the 'active' state
		 */
		private void removeAndDeactivateActiveElement()
		{
			GameObject removed = _gameObjects.remove(index);
			((gameObject.GameObject) removed).deactivate();
			_gameObjects.addLast(removed);
		}
		
		private void updateRemoveStatusToReflectRemove()
		{
			justRemoved = true;
		}
		
		private void checkVersions()
		{
			if(myIteratorVersion != Iterator_Version)
			{
				throw new IllegalStateException("two or more iterators conflicting!");
			}
		}
		private void checkCanCallNext()
		{
			if(!hasNext())
			{
				throw new IllegalStateException("Must call HASNEXT() before calling next()!");
			}
		}
		private void checkCanCallRemove()
		{
			if(justRemoved)
			{
				throw new IllegalStateException("need to call NEXT() before another remove!");
			}
		}
		private void incrementVersions()
		{
			if(Iterator_Version == Integer.MAX_VALUE)
			{
				Iterator_Version = 0;
			}
			else
			{
				Iterator_Version++;
			}
			myIteratorVersion = Iterator_Version;
		}
	}

}
